using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Diagnostics;
using System.Xml;
using System.Xml.Schema; 
using XPathParser; 
using System.Xml.Linq; 
using System.Xml.XPath;

namespace CodeGenerationPipelineInterface
{
	[DebuggerDisplay("{ DebugInfo }")]
	public class File : IXmlElement
	{
        private IXmlDocument document;
        private XNode node;
        private BuildAction buildAction;
        private Type type;
        private SourcePath sourcePath;
        private SuggestedTargetRelativePath suggestedTargetRelativePath;
        private CustomTool customTool;
        private CustomNamespace customNamespace;
        private CopyToOutputDirectory copyToOutputDirectory;
        private bool mergeWithExisting; 
        private const int unbounded = int.MaxValue;

		public File(IXmlDocument document)
		{
            
            this.document = document;
		}

        public IXmlDocument Document 
        { 
            get
            {
                return document;
            }

            set
            {
                document = value;
            }
        }

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File[@MergeWithExisting]")]
		[UseAttribute(XPathParser.Use.Required)]
		public bool MergeWithExisting 
		{
            get
            {
			    return mergeWithExisting;
            }

            set
            {
                mergeWithExisting = value;
            }
		}

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File/BuildAction")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public BuildAction BuildAction
		{
			get
			{
				return buildAction;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File/Type")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public Type Type
		{
			get
			{
				return type;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File/SourcePath")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public SourcePath SourcePath
		{
			get
			{
				return sourcePath;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File/SuggestedTargetRelativePath")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public SuggestedTargetRelativePath SuggestedTargetRelativePath
		{
			get
			{
				return suggestedTargetRelativePath;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File/CustomTool")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public CustomTool CustomTool
		{
			get
			{
				return customTool;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File/CustomNamespace")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public CustomNamespace CustomNamespace
		{
			get
			{
				return customNamespace;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File/CopyToOutputDirectory")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public CopyToOutputDirectory CopyToOutputDirectory
		{
			get
			{
				return copyToOutputDirectory;
			}
		}

		public string ElementName
		{
			get
			{
				return "File";
			}
		}

		public string DebugInfo
		{
			get
			{
				return "File";
			}
		}

        public XNode Node
        {
            get
            {
                return node;
            }
        }

        public IEnumerable<string> CDataElements
        {
            get
            {
                var element = (XElement)node;

                return element.Nodes().Where(n => n is XCData).Cast<XCData>().Select(c => c.Value);
            }
        }

        public void Read(XNode node)
        {
            var elementNode = (XElement) node;
            var prefix = elementNode.GetPrefixOfNamespace(elementNode.Name.Namespace);

            // BuildAction

            var elementBuildAction = node.XPathSelectElement(prefix + ":BuildAction", this.Document.NamespaceManager);
            var buildAction = new BuildAction(this.Document);

            buildAction.Read(elementBuildAction);

            this.buildAction  = buildAction;

            // Type

            var elementType = node.XPathSelectElement(prefix + ":Type", this.Document.NamespaceManager);
            var type = new Type(this.Document);

            type.Read(elementType);

            this.type  = type;

            // SourcePath

            var elementSourcePath = node.XPathSelectElement(prefix + ":SourcePath", this.Document.NamespaceManager);
            var sourcePath = new SourcePath(this.Document);

            sourcePath.Read(elementSourcePath);

            this.sourcePath  = sourcePath;

            // SuggestedTargetRelativePath

            var elementSuggestedTargetRelativePath = node.XPathSelectElement(prefix + ":SuggestedTargetRelativePath", this.Document.NamespaceManager);
            var suggestedTargetRelativePath = new SuggestedTargetRelativePath(this.Document);

            suggestedTargetRelativePath.Read(elementSuggestedTargetRelativePath);

            this.suggestedTargetRelativePath  = suggestedTargetRelativePath;

            // CustomTool

            var elementCustomTool = node.XPathSelectElement(prefix + ":CustomTool", this.Document.NamespaceManager);
            var customTool = new CustomTool(this.Document);

            customTool.Read(elementCustomTool);

            this.customTool  = customTool;

            // CustomNamespace

            var elementCustomNamespace = node.XPathSelectElement(prefix + ":CustomNamespace", this.Document.NamespaceManager);
            var customNamespace = new CustomNamespace(this.Document);

            customNamespace.Read(elementCustomNamespace);

            this.customNamespace  = customNamespace;

            // CopyToOutputDirectory

            var elementCopyToOutputDirectory = node.XPathSelectElement(prefix + ":CopyToOutputDirectory", this.Document.NamespaceManager);
            var copyToOutputDirectory = new CopyToOutputDirectory(this.Document);

            copyToOutputDirectory.Read(elementCopyToOutputDirectory);

            this.copyToOutputDirectory  = copyToOutputDirectory;


            // MergeWithExisting

            if (elementNode.Attributes().Any(a => a.Name.LocalName == "MergeWithExisting"))
            {
                mergeWithExisting = bool.Parse(elementNode.Attribute("MergeWithExisting").Value);
            }
            this.node = node;
        }

        public void Write(XNode parentNode, XNamespace elementNamespace)
        {
            var parentElement = (XElement)parentNode;
            var element = new XElement(elementNamespace + this.ElementName);

            parentElement.Add(element);

            // MergeWithExisting

            element.Add(new XAttribute("MergeWithExisting", this.MergeWithExisting.ToString().ToLower()));

            var buildAction = this.BuildAction;

            buildAction.Write(element, elementNamespace);
            var type = this.Type;

            type.Write(element, elementNamespace);
            var sourcePath = this.SourcePath;

            sourcePath.Write(element, elementNamespace);
            var suggestedTargetRelativePath = this.SuggestedTargetRelativePath;

            suggestedTargetRelativePath.Write(element, elementNamespace);
            var customTool = this.CustomTool;

            customTool.Write(element, elementNamespace);
            var customNamespace = this.CustomNamespace;

            customNamespace.Write(element, elementNamespace);
            var copyToOutputDirectory = this.CopyToOutputDirectory;

            copyToOutputDirectory.Write(element, elementNamespace);
        }

        public void Write(XDocument document, XmlNamespaceManager namespaces)
        {
            var namespaceAttributes = namespaces.Cast<string>().Where(s => !string.IsNullOrEmpty(s) && s != "xmlns" && s != "xml").Select(s => new XAttribute(XNamespace.Xmlns + s, XNamespace.Get(namespaces.LookupNamespace(s))));
            var element = new XElement(this.document.ElementNamespace + this.ElementName, namespaceAttributes.ToArray<object>());

            document.Add(element);

            var buildAction = this.BuildAction;
            buildAction.Write(element, this.document.ElementNamespace);

            var type = this.Type;
            type.Write(element, this.document.ElementNamespace);

            var sourcePath = this.SourcePath;
            sourcePath.Write(element, this.document.ElementNamespace);

            var suggestedTargetRelativePath = this.SuggestedTargetRelativePath;
            suggestedTargetRelativePath.Write(element, this.document.ElementNamespace);

            var customTool = this.CustomTool;
            customTool.Write(element, this.document.ElementNamespace);

            var customNamespace = this.CustomNamespace;
            customNamespace.Write(element, this.document.ElementNamespace);

            var copyToOutputDirectory = this.CopyToOutputDirectory;
            copyToOutputDirectory.Write(element, this.document.ElementNamespace);

        }

        public bool Validate(object sender, ValidationEventHandler handler)
        {
            var valid = true;
            BuildAction buildAction = this.BuildAction;
            buildAction.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            Type type = this.Type;
            type.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            SourcePath sourcePath = this.SourcePath;
            sourcePath.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            SuggestedTargetRelativePath suggestedTargetRelativePath = this.SuggestedTargetRelativePath;
            suggestedTargetRelativePath.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            CustomTool customTool = this.CustomTool;
            customTool.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            CustomNamespace customNamespace = this.CustomNamespace;
            customNamespace.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            CopyToOutputDirectory copyToOutputDirectory = this.CopyToOutputDirectory;
            copyToOutputDirectory.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            return valid;
        }
    }
}
